package me.landery.modules.sinco.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.druid.util.HttpClientUtils;
import com.alibaba.fastjson.JSONObject;
import com.sun.deploy.net.HttpUtils;
import com.sun.xml.bind.v2.runtime.output.Encoded;
import lombok.RequiredArgsConstructor;
import me.landery.exception.BadRequestException;
import me.landery.modules.sinco.domain.Application;
import me.landery.modules.sinco.domain.Call;
import me.landery.modules.sinco.domain.Mode;
import me.landery.modules.sinco.domain.em.GenType;
import me.landery.modules.sinco.service.*;
import me.landery.modules.sinco.service.dto.GeneratePaperDto;
import me.landery.modules.sinco.service.dto.VipDto;
import me.landery.modules.sinco.service.mapstruct.VipMapper;
import me.landery.modules.sinco.util.CalculateUtil;
import me.landery.modules.system.service.UserService;
import me.landery.modules.system.service.mapstruct.UserMapper;
import me.landery.utils.RedisUtils;
import me.landery.utils.SecurityUtils;
import me.landery.utils.SecurityUtils;
import me.landery.utils.SecurityUtils;
import me.landery.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.Socket;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;
/**
 * @Author cc.w
 * @Date 2022/6/7 16:55
 * @Version 1.0
 */
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "paper")
public class PaperServiceImpl implements PaperService {

    private  final UserService userService;
    private final UserMapper userMapper;
    private final CallService callService;
    private final VipService vipService;
    private final VipMapper vipMapper;
    private final ModeService modeService;
    private final RedisUtils redisUtils;
    private final ApplicationService applicationService;

    @Value("${module.IP}")
    private String moduleIP;

    @Value("${module.PORT}")
    private String modulePORT;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateResult(VipDto userVip,GeneratePaperDto generatePaperDto, Application application,HttpServletRequest request) {

        // 判断当前vip费用等
        //1. 先看套餐id是否为0，如果为0，看余额是否够
        Double balance = userVip.getBalance();
        Date expiration = userVip.getExpiration();
        Date now_time = new Date();// 当前时间

        String result;
        if (userVip.getId() == 0){
            if(balance >= userVip.getPrice()){
                result = this.saveResult(generatePaperDto,userVip.getPrice(),0L,userVip,application,request);
            }else {
                throw new BadRequestException("余额不够，请充值");
            }

        }
        //2. 不为零，先看时间，时间是否过期
        //2.1 时间过期，看余额
        else if(now_time.getTime() > expiration.getTime() ){

            //2.1.1 余额充足，原价扣钱，调用生成
            //获取套餐0的价格
            Mode mode  = modeService.findById(0L);
            Double needPrice = CalculateUtil.calculatePrice(mode);
            if (balance >= needPrice){
                result = this.saveResult(generatePaperDto,needPrice,0L,userVip,application,request);

            }else{
                //2.1.2 余额不足，调用失败
                throw new BadRequestException("余额不够，请充值");
            }

        }
        //2.2 时间未过期，先看免费额度
        else {
            // 2.2.1 免费额度为零，余额是否够
            if (userVip.getQuota() <= 0 ){
                if (balance >= userVip.getPrice()){

                    result = this.saveResult(generatePaperDto,userVip.getPrice(),0L,userVip,application,request);
                }
                else{
                    throw new BadRequestException("余额不够，请充值");
                }
            }
            //2.2.2 免费额度不为零，扣免费额度1次，生成标题、记录调用记录、
            else {
                // 扣除免费额度
                result = this.saveResult(generatePaperDto,0.0,1L,userVip,application,request);
            }
        }

        //返回生成的result
        return result;
    }

    /**
     * API调用入口
     * @param generatePaperDto 前台参数
     * @param request 前台请求，用于获取ip信息
     * @return 生成结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateResultByToken(GeneratePaperDto generatePaperDto, HttpServletRequest request) {

        //如果token为空，则抛出异常
        if (StringUtils.isBlank(generatePaperDto.getToken())){
            throw new BadRequestException("token不能为空");
        }
        //如果appId为空，则抛出异常
        if (StringUtils.isBlank(generatePaperDto.getAppId())){
            throw new BadRequestException("appId不能为空");
        }

        String paper = generatePaperDto.getPaper();
        //文章长度 200-10000之间
        if (paper.length() > 10000){
            throw new BadRequestException("您的文章长度超过最大限制，请修改后上传");
        }
        if (paper.length() < 200){
            throw new BadRequestException("您的文章长度小于最小限制，请修改后上传");
        }


        //校验token
        String token = (String)redisUtils.get(generatePaperDto.getAppId());
        if (StringUtils.isBlank(token)){
            throw new BadRequestException("token已过期");
        }
        if (!token.equals(generatePaperDto.getToken())){
            throw new BadRequestException("token认证错误");
        }
        //获取当前应用
        Application application = applicationService.getByAppId(generatePaperDto.getAppId());
        if (application == null){
            throw new BadRequestException("应用不存在");
        }
        //判断应用状态
        if (application.getStatus() == 0) {
            throw new BadRequestException("应用已禁用");
        }
        // 查询当前用户的套餐等
        VipDto userVip = vipService.findByUserID(application.getUser().getId());

        return this.generateResult(userVip,generatePaperDto,application,request);
    }

    /**
     * 需要登陆的调用入口
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateResultNeedLogin(GeneratePaperDto generatePaperDto, HttpServletRequest request){
        //获取当前用户
        Long userId = SecurityUtils.getCurrentUserId();
        // 查询当前用户的套餐等
        VipDto userVip = vipService.findByUserID(userId);
        return  this.generateResult(userVip,generatePaperDto,null,request);
    }

    /**
     * 统一调用入口
     * @param generatePaperDto 前端传过来的值
     * @param request 请求
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject generateResultEntrance(GeneratePaperDto generatePaperDto, HttpServletRequest request) {
        String result = "";
        JSONObject jsonObject = new JSONObject();
        try{
            if (StringUtils.isNotBlank(generatePaperDto.getToken())){  //不需要登录
                result  = this.generateResultByToken(generatePaperDto,request);
            }else {
                result = this.generateResultNeedLogin(generatePaperDto,request);
            }
        }
        catch (BadRequestException e){
            jsonObject.put("code",-1);
            jsonObject.put("msg",e.getMessage());
            return jsonObject;
        }
        catch (Exception e){
            jsonObject.put("code",-2);
            jsonObject.put("msg","系统异常");
            return jsonObject;
        }
        if (StringUtils.isBlank(result)){
            jsonObject.put("code",-1);
            jsonObject.put("msg","生成失败");
            return jsonObject;
        }
        jsonObject.put("code",0);
        jsonObject.put("msg","success");
        jsonObject.put("data",result);

        return jsonObject;
    }


    /**
     *
     * @param generatePaperDto  前台参数
     * @param balance 需要扣的余额
     * @param quota 需要扣除的免费额度
     * @param userVip 用户vip
     * @return 生成内容
     */

    @Transactional(rollbackFor = Exception.class)
    public String saveResult(GeneratePaperDto generatePaperDto, Double balance, Long quota, VipDto userVip,Application application, HttpServletRequest request){

        // 生成结果，
        boolean success = true;
        String result = "";
        String type  = "";
        Integer typeid = generatePaperDto.getTypeid();
        try {
//            type = GenType.ABSTRACT.getName();


            if (Objects.equals(typeid, GenType.TITLE.getValue())){ // 标题
                type = GenType.TITLE.getName();
//                result = getResultFromModel(generatePaperDto);

            }else if (Objects.equals(typeid, GenType.ABSTRACT.getValue())){
                type = GenType.ABSTRACT.getName();
//                result = getResultFromModel(generatePaperDto);
            }else if (Objects.equals(typeid, GenType.SENTIMENT.getValue())) {
                type = GenType.SENTIMENT.getName();
            }else if (Objects.equals(typeid, GenType.ILLEGAL.getValue())) {
                type = GenType.ILLEGAL.getName();
            }else if (Objects.equals(typeid, GenType.ENTITY.getValue())) {
                type = GenType.ENTITY.getName();
            }else if (Objects.equals(typeid, GenType.RELIGION.getValue())) {
                type = GenType.RELIGION.getName();
            }else {
                throw new BadRequestException("请选择正确的类型");
            }

            result = getResultFromModel(generatePaperDto);

        }catch (Exception e){
            success = false;
            result = "";
            e.printStackTrace();
        }
        if (success){
            // 成功扣费
            userVip.setQuota(userVip.getQuota()-quota);
            userVip.setBalance(userVip.getBalance()-balance);
            vipService.save(vipMapper.toEntity(userVip));
        }else {
            //失败的话 不扣除免费额度，不花费余额
            balance = 0.0;
            quota = 0L;
        }

        // 保存调用记录
        Call call1 = new Call();
        call1.setContent(generatePaperDto.getPaper());
        call1.setTitle(generatePaperDto.getTitle());
        call1.setGenerateType(generatePaperDto.getGenerateType());
        call1.setLanguage(generatePaperDto.getLanguage());
        call1.setContentType(generatePaperDto.getContentType());
        call1.setResult(result);
        call1.setType(type);
        call1.setSuccess(success);
        call1.setTime(new Date());
        call1.setLength(generatePaperDto.getPaper().length());
        if (application == null) {
            call1.setUser(userMapper.toEntity(userService.findById(SecurityUtils.getCurrentUserId())));
        }else {
            call1.setUser(application.getUser());
        }

        call1.setCost(balance);
        call1.setQuota(quota);
        call1.setApplication(application);

        //获取调用者ip地址
        String ipaddr = StringUtils.getIp(request);
        call1.setIpaddr(ipaddr);
        call1.setAddr(StringUtils.getCityInfo(ipaddr));
        callService.save(call1);

        return result;

    }
    // 从模型训练获取结果

    private  String  getResultFromModel(GeneratePaperDto generatePaperDto){
        //获取开始时间
        long startTime=System.currentTimeMillis();
        //测试的代码段
        String result = remoteCallHttp(generatePaperDto);
        //获取结束时间
        long endTime=System.currentTimeMillis();
        System.out.println("程序运行时间： "+(endTime-startTime));

        return result ;
    }

    /**
     * 调用模型
     * @param generatePaperDto
     * @return
     */
    private String remoteCallHttp(GeneratePaperDto generatePaperDto){
        String result = "";
        try {
            String url = "http://121.48.227.135:6667/generate_result";
            result  =  HttpRequest.post(url).body(JSONObject.toJSONString(generatePaperDto)).execute().body();
            System.out.println(result);
            JSONObject jsonObject = JSONObject.parseObject(result);
            if (jsonObject.get("status").equals("success")){
                result = new String(jsonObject.getString("result").getBytes(),"UTF-8");
            }else {
                throw new BadRequestException("调用模型失败");
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("调用远程接口失败");
        }
        return result;
    }

//    // 从模型训练获取文章摘要
//    private  String  getAbstractFromModel(String paper){
//        return paper;
//    }

    private String remoteCall(GeneratePaperDto generatePaperDto){
        // 访问服务进程的套接字
        Socket socket = null;
        String HOST = moduleIP;
        String PORT = modulePORT;
        System.out.println("调用远程接口:host=>"+HOST+",port=>"+PORT);
        try {
            // 初始化套接字，设置访问服务的主机和进程端口号，HOST是访问python进程的主机名称，可以是IP地址或者域名，PORT是python进程绑定的端口号
            socket = new Socket(HOST, Integer.parseInt(PORT));
            // 获取输出流对象
            OutputStream os = socket.getOutputStream();
            PrintStream out = new PrintStream(os);
            // 发送内容
            out.print(JSONObject.toJSONString(generatePaperDto));
            // 告诉服务进程，内容发送完毕，可以开始处理
            out.print("over");
            socket.shutdownOutput();

            // 获取服务进程的输入流
            byte[] bytes = new byte[1024];
            int len;

            // 获得输入流
            InputStream inputStream = socket.getInputStream();
            StringBuilder sb = new StringBuilder();
            while ((len = inputStream.read(bytes)) != -1) {
                // 注意指定编码格式，发送方和接收方一定要统一，建议使用UTF-8
                sb.append(new String(bytes, 0, len, "UTF-8"));
            }
            // 解析结果
            System.out.println(sb);
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("调用远程接口失败");
        } finally {
            try {
                if(socket!=null) {
                    socket.close();
                }
            } catch (IOException ignored) {

            }
            System.out.println("远程接口调用结束.");
        }
    }
}
